package com.corpgovernment.organization.service.impl;

import com.corpgovernment.api.applytrip.enums.ApplyTripEmployeeEnum;
import com.corpgovernment.api.applytrip.soa.request.ApplyTripPersonRequest;
import com.corpgovernment.api.applytrip.soa.response.ApplyTripPersonResponse;
import com.corpgovernment.api.costcenter.model.CostCenter;
import com.corpgovernment.api.costcenter.service.ICostCenterService;
import com.corpgovernment.api.organization.enums.CardTypeEnum;
import com.corpgovernment.api.organization.model.ffpinfo.GetFfBasicsInfoResponse;
import com.corpgovernment.api.organization.model.org.OrgConfig;
import com.corpgovernment.api.organization.model.org.OrgConfigInfoVo;
import com.corpgovernment.api.organization.model.org.request.GetOrgConfigBatchRequest;
import com.corpgovernment.api.organization.model.org.request.GetOrgConfigRequest;
import com.corpgovernment.api.organization.model.org.response.GetOrgConfigBatchResponse;
import com.corpgovernment.api.organization.model.org.response.GetOrgConfigResponse;
import com.corpgovernment.api.organization.model.passenger.PassengerVo;
import com.corpgovernment.common.apollo.OrganizationApollo;
import com.corpgovernment.common.base.BaseRequestVO.UserInfo;
import com.corpgovernment.common.base.BaseService;
import com.corpgovernment.organization.constant.CommonConst;
import com.corpgovernment.organization.convert.PassengerConvert;
import com.corpgovernment.organization.dataloader.ApplyTripClientLoader;
import com.corpgovernment.organization.dataloader.db.*;
import com.corpgovernment.organization.entity.db.*;
import com.corpgovernment.organization.service.IOrgConfigService;
import com.corpgovernment.organization.service.IOrgUserIdCardService;
import com.corpgovernment.organization.service.IOrganizationInfoService;
import com.corpgovernment.organization.service.IPassengerService;
import com.corpgovernment.organization.vo.GetPassengerRequestVO;
import com.corpgovernment.organization.vo.GetPassengerResponseVO;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author jhxue
 * @create 2022-03-01-16:09
 */
@Slf4j
public abstract class AbstractPassengerService extends BaseService implements IPassengerService {


	@Autowired
	protected PassengerConvert passengerConvert;
	@Autowired
	protected MbOrgEmployeeLoader mbOrgEmployeeLoader;
	@Autowired
	protected MbOrgInfoLoader mbOrgInfoLoader;
	@Autowired
	protected IOrgUserIdCardService orgUserIdCardService;
	@Autowired
	protected MbOrgUserIdcardLoader mbOrgUserIdcardLoader;
	@Autowired
	protected MbOrgNonEmployeeLoader mbOrgNonEmployeeLoader;
	@Autowired
	protected ICostCenterService costCenterService;
	@Autowired
	protected OrganizationApollo organizationApollo;
	@Autowired
	protected MbFreshPassengerLoader freshPassengerLoader;
	@Autowired
	protected IOrganizationInfoService organizationInfoService;
	@Autowired
	private ApplyTripClientLoader applyTripClientLoader;
	@Autowired
	private IOrgConfigService orgConfigService;
	@Override
	public abstract GetPassengerResponseVO getPassenger(GetPassengerRequestVO request);
    @Autowired
    @Qualifier(value = "getValidPassengerThreadPoolExecutor")
    private ThreadPoolExecutor getValidPassengerThreadPoolExecutor;

	@Override
	public abstract String getType();

	/**
	 * 排序
	 *
	 * @param passengerList
	 * @param userInfo
	 * @return
	 */
	protected List<PassengerVo> sort(List<PassengerVo> passengerList, UserInfo userInfo) {
		String uid = userInfo.getUid();
		String orgId = userInfo.getOrgId();
		List<PassengerVo> subPassengerList = passengerList.stream().filter(e -> Objects.equals(e.getUid(), uid)).collect(Collectors.toList());
		passengerList.removeAll(subPassengerList);
		List<PassengerVo> passengerList2 = subPassengerList.stream().filter(e -> !Objects.equals(e.getOrgId(), orgId)).collect(Collectors.toList());
		passengerList.addAll(0, passengerList2);
		subPassengerList.stream().filter(e -> Objects.equals(e.getOrgId(), orgId)).findFirst().ifPresent(p -> {
			passengerList.add(0, p);
		});
		return passengerList;
	}

	/**
	 * 获取员工信息
	 *
	 * @param passengerList
	 * @param request
	 * @return
	 */
	protected List<PassengerVo> packagePassengerList(List<PassengerVo> passengerList, GetPassengerRequestVO request) {
		if (CollectionUtils.isEmpty(passengerList)) {
			addElkInfoLog("出行人为空，该日志不会存在");
			return new ArrayList<>();
		}
		String site = request.getSite();
		List<String> uids = passengerList.stream().map(PassengerVo::getUid).filter(Objects::nonNull).distinct().collect(Collectors.toList());
		List<String> orgIds = passengerList.stream().map(PassengerVo::getOrgId).distinct().collect(Collectors.toList());
		addElkInfoLog("出行人组织id集合：%s", JsonUtils.toJsonString(orgIds));
        CompletableFuture<Map<String, MbOrgInfo>> orgInfoFuture = CompletableFuture.supplyAsync(() -> queryOrgInfo(orgIds), getValidPassengerThreadPoolExecutor);
		List<PassengerVo> result = new ArrayList<>();
		Map<String, GetOrgConfigResponse> orgConfigMap = new HashedMap();
		if (CollectionUtils.isNotEmpty(uids)) {
		    CompletableFuture<Map<String, MbOrgEmployeeInfo>> employeeInfoFuture = CompletableFuture.supplyAsync(() -> queryEmployeeInfo(uids), getValidPassengerThreadPoolExecutor);
            CompletableFuture<Map<String, List<MbOrgUserIdcard>>> userCardInfoFuture = CompletableFuture.supplyAsync(() -> queryEmployeeCardInfo(uids), getValidPassengerThreadPoolExecutor);
            CompletableFuture<Map<String, List<CostCenter>>> costCenterInfoFuture = CompletableFuture.supplyAsync(() -> costCenterService.getCostCenterBatch(orgIds), getValidPassengerThreadPoolExecutor);
            CompletableFuture<Map<String, GetOrgConfigResponse>> orgConfigInfoFuture = CompletableFuture.supplyAsync(() -> queryOrgConfigInfo(orgIds), getValidPassengerThreadPoolExecutor);
            Map<String, CostCenter> costMap = Maps.newHashMap();
			List<PassengerVo> employeeList = new ArrayList<>();
            Map<String, MbOrgInfo> orgInfoMap = CollectionUtils.newHashMap();
            Map<String, List<MbOrgUserIdcard>> uidCardMap = CollectionUtils.newHashMap();
            Map<String, MbOrgEmployeeInfo> employeeMap = CollectionUtils.newHashMap();
            Map<String, List<CostCenter>> costCenterMap = CollectionUtils.newHashMap();
            Map<String, GetOrgConfigResponse> orgConfigInfoMap = CollectionUtils.newHashMap();
			try {
                orgInfoMap = orgInfoFuture.get();
                employeeMap = employeeInfoFuture.get();
                uidCardMap = userCardInfoFuture.get();
                costCenterMap = costCenterInfoFuture.get();
                orgConfigInfoMap = orgConfigInfoFuture.get();
            } catch (Exception e){
			    log.error("异步获取数据失败!", e);
            }
			for (PassengerVo vo : passengerList) {
				if (StringUtils.isNotBlank(vo.getUid())) {
					String orgId = vo.getOrgId();
					String uid = vo.getUid();
					MbOrgInfo orgInfo = orgInfoMap.get(orgId);
					List<MbOrgUserIdcard> cardList = uidCardMap.get(uid);
					MbOrgEmployeeInfo employeeInfo = employeeMap.get(uid);
					if (Objects.nonNull(employeeInfo)) {
						PassengerVo passenger = passengerConvert.convertFromEmployee(employeeInfo, orgInfo, cardList, site);
						passenger.setId(vo.getId());
						if (costMap.get(orgId) == null) {
							List<CostCenter> costCenters = costCenterMap.get(orgId);
							if (CollectionUtils.isNotEmpty(costCenters)){
								Optional<CostCenter> first = costCenters.stream().filter(item -> BooleanUtils.isTrue(item.getIsDefault())).findFirst();
								if (first.isPresent()){
									costMap.putIfAbsent(orgId, first.get());
								} else {
									costMap.putIfAbsent(orgId, costCenters.get(0));
								}
							}
						}
						CostCenter costCenter = costMap.get(orgId);

						passenger.setCostCenter(costCenter);
						passenger.setPageEnd(vo.getPageEnd());

						GetOrgConfigResponse getOrgConfigResponse = orgConfigMap.get(orgId);
						if (Objects.isNull(getOrgConfigResponse)){
							GetOrgConfigRequest getOrgConfigRequest = new GetOrgConfigRequest();
							getOrgConfigRequest.setOrgId(orgId);
							getOrgConfigResponse = orgConfigInfoMap.get(orgId);
							orgConfigMap.put(orgId, getOrgConfigResponse);
						}
						//增加管控开关
						if(StringUtils.isNotBlank(request.getTravelId())) {
							passenger.setProjectIsRequired(false);
							passenger.setCostCenterIsRequired(false);
						} else if (Objects.nonNull(getOrgConfigResponse)){
							passenger.setProjectIsRequired(getOrgConfigResponse.getOrgConfigInfo().getProjectIsVerify());
							passenger.setCostCenterIsRequired(getOrgConfigResponse.getOrgConfigInfo().getCostCenterIsVerify());
						}

						ApplyTripEmployeeEnum applyTripEmployeeEnum = ApplyTripEmployeeEnum.getByCode(employeeInfo.getEmployeeType());
						passenger.setEmployeeType(Optional.ofNullable(applyTripEmployeeEnum).map(ApplyTripEmployeeEnum::getCode).orElse(ApplyTripEmployeeEnum.EMPLOYEE.getCode()));
						passenger.setEmployeeDesc(Optional.ofNullable(applyTripEmployeeEnum).map(ApplyTripEmployeeEnum::getName).orElse(ApplyTripEmployeeEnum.EMPLOYEE.getName()));
						employeeList.add(passenger);
					}
				}
			}
			addElkInfoLog("员工集合：%s", JsonUtils.toJsonString(employeeList));
			result.addAll(employeeList);
		}
		List<Long> nonEmployeeUids = passengerList.stream().map(PassengerVo::getNoEmployeeId).filter(Objects::nonNull).map(Long::valueOf).collect(Collectors.toList());
		addElkInfoLog("非员工id集合：%s", JsonUtils.toJsonString(nonEmployeeUids));
		if (CollectionUtils.isNotEmpty(nonEmployeeUids)) {
            CompletableFuture<Map<Long, MbOrgNonEmployeeInfo>> nonEmployeeInfoFuture = CompletableFuture.supplyAsync(() -> queryNonEmployeeInfo(nonEmployeeUids), getValidPassengerThreadPoolExecutor);
            CompletableFuture<Map<Long, List<MbOrgUserIdcard>>> nonEmployeeCardInfoFuture = CompletableFuture.supplyAsync(() -> queryNonEmployeeCardInfo(nonEmployeeUids), getValidPassengerThreadPoolExecutor);
            Map<Long, MbOrgNonEmployeeInfo> nonEmployeeMap = CollectionUtils.newHashMap();
            Map<Long, List<MbOrgUserIdcard>> uidCardMap = CollectionUtils.newHashMap();
			try {
                nonEmployeeMap = nonEmployeeInfoFuture.get();
                uidCardMap = nonEmployeeCardInfoFuture.get();
            } catch (Exception e){
                log.error("异步获取数据失败!", e);
            }
            GetOrgConfigRequest getOrgConfigRequest = new GetOrgConfigRequest();
            getOrgConfigRequest.setOrgId(request.getLoginOrgId());
            GetOrgConfigResponse loginOrgConfig = orgConfigService.getOrgConfig(getOrgConfigRequest);
            Map<Long, MbOrgNonEmployeeInfo> finalNonEmployeeMap = nonEmployeeMap;
            Map<Long, List<MbOrgUserIdcard>> finalUidCardMap = uidCardMap;
            List<PassengerVo> nonEmployeeList = passengerList.stream().filter(e -> StringUtils.isNotBlank(e.getNoEmployeeId())
			&& Objects.nonNull(finalNonEmployeeMap.get(Long.valueOf(e.getNoEmployeeId()))) ).map(e -> {
				Long nonEmployeeId = Long.valueOf(e.getNoEmployeeId());
				List<MbOrgUserIdcard> cardList = finalUidCardMap.get(nonEmployeeId);
				MbOrgNonEmployeeInfo nonEmployeeInfo = finalNonEmployeeMap.get(nonEmployeeId);
				PassengerVo passenger = passengerConvert.convertFromNonEmployee(nonEmployeeInfo, cardList, site);
				passenger.setId(e.getId());

				passenger.setCostCenter(e.getCostCenter());
				passenger.setCostCenterCode(e.getCostCenterCode());
				passenger.setCostCenterName(e.getCostCenterName());

				passenger.setPageEnd(e.getPageEnd());

				GetOrgConfigResponse getOrgConfigResponse = orgConfigMap.get(request.getLoginOrgId());
				if (Objects.isNull(getOrgConfigResponse)){
					getOrgConfigResponse = loginOrgConfig;
					orgConfigMap.put(request.getLoginOrgId(), getOrgConfigResponse);
				}
				//增加管控开关
				if(StringUtils.isNotBlank(request.getTravelId())) {
					passenger.setProjectIsRequired(false);
					passenger.setCostCenterIsRequired(false);
				} else {
					//增加管控开关
					passenger.setProjectIsRequired(getOrgConfigResponse.getOrgConfigInfo().getProjectIsVerify());
					passenger.setCostCenterIsRequired(getOrgConfigResponse.getOrgConfigInfo().getCostCenterIsVerify());
				}

				passenger.setEmployeeType(ApplyTripEmployeeEnum.EXTERNAL_EMPLOYEE.getCode());
				passenger.setEmployeeDesc(ApplyTripEmployeeEnum.EXTERNAL_EMPLOYEE.getName());


				return passenger;
			}).collect(Collectors.toList());
			addElkInfoLog("非员工集合：%s", JsonUtils.toJsonString(nonEmployeeList));
			result.addAll(nonEmployeeList);
		}
		if (CollectionUtils.isNotEmpty(result) && StringUtils.isNotBlank(request.getTravelId())){
			ApplyTripPersonRequest applyTripPersonRequest = new ApplyTripPersonRequest();
			applyTripPersonRequest.setApplyNo(request.getTravelId());
			applyTripPersonRequest.setTravelId(request.getTripId());
			List<ApplyTripPersonResponse> applyTripPerson = applyTripClientLoader.getApplyTripPerson(applyTripPersonRequest);
			Map<String, ApplyTripPersonResponse> applyTripPersonMap = applyTripPerson.stream().collect(Collectors.toMap(item -> item.getUid(), Function.identity(), (k1,k2) -> k1));
			result.forEach(item ->{
				String userId = item.getUid();
				if (StringUtils.isBlank(userId)){
					userId = item.getNoEmployeeId();
				}
				ApplyTripPersonResponse personResponse = applyTripPersonMap.get(userId);
				if (personResponse != null){
					item.setWbsRemark(personResponse.getWbsRemark());
					item.setCostCenterRemark(personResponse.getCostCenterRemark());

					item.setCostCenterCode(personResponse.getCostCenterCode());
					item.setCostCenterName(personResponse.getCostCenterDesc());
					item.setCostCenterId(personResponse.getCostCenterId());
					item.setProjectId(personResponse.getProjectId());
					item.setProjectCode(personResponse.getProjectCode());
					item.setProjectName(personResponse.getProjectName());
					item.setNoSelectProjectDesc(personResponse.getNoSelectProjectDesc());

					PassengerVo.ProjectInfo projectInfo = new PassengerVo.ProjectInfo();
					projectInfo.setProjectId(personResponse.getProjectId());
					projectInfo.setProjectCode(personResponse.getProjectCode());
					projectInfo.setProjectName(personResponse.getProjectName());
					projectInfo.setNoSelectProjectDesc(personResponse.getNoSelectProjectDesc());
					item.setProjectInfo(projectInfo);

					CostCenter costCenter = new CostCenter();
					costCenter.setCostCenterCode(personResponse.getCostCenterCode());
					costCenter.setCostCenterName(personResponse.getCostCenterDesc());
					costCenter.setCostCenterId(personResponse.getCostCenterId());
					costCenter.setId(StringUtils.isNotBlank(personResponse.getCostCenterId()) ? Integer.parseInt(personResponse.getCostCenterId()) : null);
					item.setCostCenter(costCenter);
				}
			});
		}
		//处理常用旅客
		return getPassengerRelationId(result, request.getUserInfo().getUid());
	}

	private Map<String, GetOrgConfigResponse> queryOrgConfigInfo(List<String> orgIds){
        GetOrgConfigBatchRequest request = new GetOrgConfigBatchRequest();
        request.setOrgIdList(orgIds);
        GetOrgConfigBatchResponse orgConfigBatch = orgConfigService.getOrgConfigBatch(request);
        if (Objects.isNull(orgConfigBatch) || CollectionUtils.isEmpty(orgConfigBatch.getOrgConfigList())){
            return CollectionUtils.newHashMap();
        }
        HashMap<String, GetOrgConfigResponse> map = new HashMap<>();
        orgIds.forEach(e ->{
            GetOrgConfigResponse config = new GetOrgConfigResponse();
            OrgConfig orgConfig = orgConfigBatch.getOrgConfigList().stream().filter(k -> e.equals(k.getOrgId())).findFirst().orElse(null);
            if (Objects.nonNull(orgConfig)){
                config.setOrgConfigInfo(orgConfig.getOrgConfigInfo());
            } else {
                OrgConfigInfoVo orgConfigInfoVo = new OrgConfigInfoVo();
                orgConfigInfoVo.setCostCenterIsRequired(CommonConst.FALSE);
                orgConfigInfoVo.setProjectIsRequired(CommonConst.FALSE);
                orgConfigInfoVo.setNameEditDuringBooking(false);
                config.setOrgConfigInfo(orgConfigInfoVo);
            }
            map.put(e, config);
        });
        return map;
    }

    private Map<Long, List<MbOrgUserIdcard>> queryNonEmployeeCardInfo(List<Long> nonEmployeeIds){
        List<MbOrgUserIdcard> userIdCards = orgUserIdCardService.batchFindByNonEmployeeUids(nonEmployeeIds);
        return userIdCards.stream().collect(Collectors.groupingBy(MbOrgUserIdcard::getNonEmployeeId));
    }

    private Map<String, MbOrgInfo> queryOrgInfo(List<String> orgIds){
        List<MbOrgInfo> mbOrgInfoList = mbOrgInfoLoader.selectByOrgIds(orgIds);
        return mbOrgInfoList.stream().collect(Collectors.toMap(MbOrgInfo::getOrgId, mbOrgInfo -> mbOrgInfo));
    }

    private Map<Long, MbOrgNonEmployeeInfo> queryNonEmployeeInfo(List<Long> nonEmployeeIds){
        List<MbOrgNonEmployeeInfo> nonEmployeeInfoList = mbOrgNonEmployeeLoader.selectByIds(nonEmployeeIds);
        return nonEmployeeInfoList.stream().collect(Collectors.toMap(MbOrgNonEmployeeInfo::getId, e -> e));
    }

    private Map<String, MbOrgEmployeeInfo> queryEmployeeInfo(List<String> uidList){
        List<MbOrgEmployeeInfo> employeeInfoList = mbOrgEmployeeLoader.selectByUids(uidList);
        return employeeInfoList.stream().collect(Collectors.toMap(MbOrgEmployeeInfo::getUid, Function.identity(), (e1, e2) -> e1));
    }

    private Map<String, List<MbOrgUserIdcard>> queryEmployeeCardInfo(List<String> uidList){
        List<MbOrgUserIdcard> mbOrgUserIdCards = mbOrgUserIdcardLoader.selectByUids(uidList);
        return mbOrgUserIdCards.stream().collect(Collectors.groupingBy(MbOrgUserIdcard::getEmployeeUid));
    }

	protected List<PassengerVo> getPassengerRelationId(List<PassengerVo> passengerDtoList, String uid) {
		List<GetFfBasicsInfoResponse> data = organizationInfoService.getFfBasicsInfo(uid);
		log.info("查询出的常旅信息集合:{}", JsonUtils.toJsonString(data));
		if (CollectionUtils.isEmpty(data)) {
			return passengerDtoList;
		}

		Map<String, List<GetFfBasicsInfoResponse>> collect = data.stream().collect(Collectors.groupingBy(GetFfBasicsInfoResponse::getFreshUid));

		for (PassengerVo passengerVo : passengerDtoList) {
			List<GetFfBasicsInfoResponse> getFfBasicsInfoResponses = collect.get(passengerVo.getUid());
			if (CollectionUtils.isEmpty(getFfBasicsInfoResponses)) {
                getFfBasicsInfoResponses = collect.get(passengerVo.getNoEmployeeId());
			}
			if (CollectionUtils.isEmpty(getFfBasicsInfoResponses)){
			    continue;
            }
			passengerVo.setRelationFlag(Boolean.TRUE);
			passengerVo.setRelationId(getFfBasicsInfoResponses.get(0).getId());
		}

		return passengerDtoList;
	}

	/**
	 * 根据名称或证件精确匹配
	 *
	 * @param query        精确查询条件
	 * @param needEmployee 是否必须为员工
	 * @return
	 */
	protected List<Long> queryByNameOrCardNo(String query, boolean needEmployee) {
		List<MbOrgUserIdcard> cardList = orgUserIdCardService.findByTypeAndNo(CardTypeEnum.ID_CARD.getCode(), query);
		if (CollectionUtils.isNotEmpty(cardList)) {
			List<Long> nonEmployeeIds = cardList.stream().map(MbOrgUserIdcard::getNonEmployeeId).filter(Objects::nonNull).collect(Collectors.toList());
			List<String> employeeIds = cardList.stream().map(MbOrgUserIdcard::getEmployeeUid).filter(org.apache.commons.lang.StringUtils::isNotBlank).collect(Collectors.toList());
			List<MbFreshPassenger> employeeList = new ArrayList<>();
			if (!needEmployee && CollectionUtils.isNotEmpty(nonEmployeeIds)) {
				employeeList.addAll(freshPassengerLoader.findByNonEmployeeIds(nonEmployeeIds));
			}
			if (CollectionUtils.isNotEmpty(employeeIds)) {
				employeeList.addAll(freshPassengerLoader.findByEmployeeIds(employeeIds));
			}
			return employeeList.stream().map(MbFreshPassenger::getId).collect(Collectors.toList());
		} else {
			return freshPassengerLoader.queryByName(query, needEmployee);
		}
	}

	/**
	 * 根据名称模糊查询
	 *
	 * @param name         模糊查询条件
	 * @param needEmployee 是否必须为员工
	 * @return
	 */
	protected List<Long> queryLikeName(String name, boolean needEmployee) {
		return freshPassengerLoader.queryLikeName(name, needEmployee);
	}

}
